#include "Serial.h"

#include "Beep.h"

static Serial *global_serial1 = NULL;
static Serial *global_serial2 = NULL;
static Serial *global_serial3 = NULL;

void
serial_init(Serial *self, SerialType s_type, SerialIRQHandler handler)
{
        self->handler = handler;
        self->flag    = 0;

        /* GPIO 初始化 */
        GPIO_InitTypeDef gpio[2];
        // TX
        gpio[0].GPIO_Mode  = GPIO_Mode_AF_PP;
        gpio[0].GPIO_Speed = GPIO_Speed_50MHz;
        // RX
        gpio[1].GPIO_Mode  = GPIO_Mode_IPU;
        gpio[1].GPIO_Speed = GPIO_Speed_50MHz;

        /*USART初始化*/
        USART_InitTypeDef usart = {.USART_HardwareFlowControl =
                                           USART_HardwareFlowControl_None,
                                   .USART_Mode = USART_Mode_Tx | USART_Mode_Rx,
                                   .USART_Parity     = USART_Parity_No,
                                   .USART_StopBits   = USART_StopBits_1,
                                   .USART_WordLength = USART_WordLength_8b};

        /*NVIC配置*/
        NVIC_InitTypeDef nvic = {.NVIC_IRQChannelCmd = ENABLE};

        switch (s_type) {
        case SERIAL_TYPE_USART1:
                self->usart          = USART1;
                usart.USART_BaudRate = 19200;
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
                gpio[0].GPIO_Pin = GPIO_Pin_9;
                gpio[1].GPIO_Pin = GPIO_Pin_10;
                GPIO_Init(GPIOA, &gpio[0]);
                GPIO_Init(GPIOA, &gpio[1]);
                nvic.NVIC_IRQChannel                   = USART1_IRQn;
                nvic.NVIC_IRQChannelPreemptionPriority = 3;
                nvic.NVIC_IRQChannelSubPriority        = 3;
                global_serial1                         = self;
                break;
        case SERIAL_TYPE_USART2:
                self->usart          = USART2;
                usart.USART_BaudRate = 115200;
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
                gpio[0].GPIO_Pin = GPIO_Pin_2;
                gpio[1].GPIO_Pin = GPIO_Pin_3;
                GPIO_Init(GPIOA, &gpio[0]);
                GPIO_Init(GPIOA, &gpio[1]);
                nvic.NVIC_IRQChannel = USART2_IRQn;
                nvic.NVIC_IRQChannelPreemptionPriority = 0;
                nvic.NVIC_IRQChannelSubPriority        = 0;
                global_serial2       = self;
                break;
        case SERIAL_TYPE_USART3:
                self->usart          = USART3;
                usart.USART_BaudRate = 115200;
                RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
                RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
                gpio[0].GPIO_Pin = GPIO_Pin_10;
                gpio[1].GPIO_Pin = GPIO_Pin_11;
                GPIO_Init(GPIOB, &gpio[0]);
                GPIO_Init(GPIOB, &gpio[1]);
                nvic.NVIC_IRQChannel = USART3_IRQn;
                global_serial3       = self;
                break;
        }

        USART_Init(self->usart, &usart);
        USART_ITConfig(self->usart, USART_IT_RXNE, ENABLE);
        // NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        NVIC_Init(&nvic);
        USART_Cmd(self->usart, ENABLE);
}

inline void
serial_send_text(const Serial *self, const uint8_t *buf)
{
        uint16_t timeout_protected = 0;

        for (short i = 0; buf[i] != '\0'; ++i) {
                while (USART_GetFlagStatus(self->usart, USART_FLAG_TXE) ==
                       RESET) {
                        timeout_protected++;
                        if (timeout_protected >= 65533) {
                                timeout_protected = 0;
                                break;
                        }
                }
                USART_SendData(self->usart, buf[i]);
                while (USART_GetFlagStatus(self->usart, USART_FLAG_TC) == RESET) {
                        timeout_protected++;
                        if (timeout_protected >= 65533) {
                                timeout_protected = 0;
                                break;
                        }
                }
        }
}

void
serial_send_byte0(const Serial *self)
{
        while (USART_GetFlagStatus(self->usart, USART_FLAG_TXE) == RESET)
                ;
        USART_SendData(self->usart, 0);
        while (USART_GetFlagStatus(self->usart, USART_FLAG_TC) == RESET)
                ;
}

void
USART1_IRQHandler(void)
{
        static uint8_t state = 0;
        static uint8_t index = 0;
        static uint8_t datas[256];

        if (!global_serial1)
                return;
        if (USART_GetITStatus(global_serial1->usart, USART_IT_RXNE) != SET)
                return;

        uint8_t data = USART_ReceiveData(global_serial1->usart);
        if (state == 0) {
                if (data == '\r') {
                        state = 2;
                } else {
                        state          = 1;
                        datas[index++] = data;
                }
        } else if (state == 1) {
                if (data == '\r')
                        state = 2;
                else
                        datas[index++] = data;
        } else if (state == 2) {
                if (data == '\n')
                        state = 0;
                datas[index] = '\0';
                index        = 0;
                if (datas[0] != '\0' && state == 0)
                        global_serial1->handler(global_serial1, datas);
        }
        if (index >= 254) {
                datas[255] = '\0';
                index      = 0;
        }
        USART_ClearITPendingBit(global_serial1->usart, USART_IT_RXNE);
}

void
USART2_IRQHandler(void)
{
        static uint8_t state = 0;
        static uint8_t index = 0;
        static uint8_t datas[256];

        if (!global_serial2)
                return;
        if (USART_GetITStatus(global_serial2->usart, USART_IT_RXNE) != SET)
                return;

        const uint8_t data = USART_ReceiveData(global_serial2->usart);
        if (state == 0) {
                if (data == '\r') {
                        state = 2;
                } else if (data == '>') {
                        datas[0] = data;
                        datas[1] = '\0';
                        global_serial2->handler(global_serial2, datas);
                } else {
                        state          = 1;
                        datas[index++] = data;
                }
        } else if (state == 1) {
                if (data == '\r')
                        state = 2;
                else
                        datas[index++] = data;
        } else if (state == 2) {
                if (data == '\n')
                        state = 0;
                datas[index] = '\0';
                index        = 0;
                if (datas[0] != '\0' && state == 0)
                        global_serial2->handler(global_serial2, datas);
        }
        if (index >= 254) {
                datas[255] = '\0';
                index      = 0;
        }
        USART_ClearITPendingBit(global_serial2->usart, USART_IT_RXNE);
}

/*
void
USART3_IRQHandler(void)
{
        static uint8_t state = 0;
        static uint8_t index = 0;
        static uint8_t datas[256];

        if (!global_serial3)
                return;
        if (USART_GetITStatus(global_serial3->usart, USART_IT_RXNE) != SET)
                return;

        uint8_t data = USART_ReceiveData(global_serial3->usart);
        if (state == 0) {
                state          = 1;
                datas[index++] = data;
        } else if (state == 1) {
                if (data == '\r')
                        state = 2;
                else
                        datas[index++] = data;
        } else if (state == 2) {
                if (data == '\n')
                        state = 0;
                datas[index] = '\0';
                index        = 0;
                if (datas[0] != '\0' && state == 0)
                        global_serial3->handler(global_serial3, datas);
        }
        USART_ClearITPendingBit(global_serial3->usart, USART_IT_RXNE);
}*/
